﻿using Microsoft.VisualStudio.TestTools;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using NetBase.View;
using Net.NetBase;

namespace NetBase
{
    public class SqImgLayer : IEnumerable<SqRt>, IDisposable
    {
        protected SqRt[,] layer;

        public int Width { get { return layer.GetLength(0); } }

        public int Height { get { return layer.GetLength(1); } }

        private delegate void XYFunction(XY xy);

        public static IEnumerable<XY> xyGen( int width, int height)
        {
            for (int i = 0; i < width; i++)
            {
                for (int j = 0; j < height; j++)
                {
                    XY val;
                    val.X = i; val.Y = j;
                    yield return val;
                }
            }
        }

        public SqImgLayer(Bitmap img) : this( img.Width, img.Height )
        {
            foreach (var xy in xyGen(Width, Height))
            {
                layer[xy.X, xy.Y].setInput(img.GetPixel(xy.X, xy.Y)); 
            }
        }

        public SqImgLayer(int width, int height)
        {
            layer = new SqRt[width, height];
            XYFunction v = (XY xy) => {layer[xy.X, xy.Y] = new SqRt(xy.X, xy.Y, this);};
            foreach( var item in xyGen(width, height))
            {
                v(item);
            }
            //Parallel.ForEach( xyGen(width, height), item => v(item));
            SetRels();
        }

        public SqRt this[int x, int y]
        {
            get
            {
                if ((x < 0) || (x >= Width) || (y < 0) || (y >= Height))
                {
                    throw new ApplicationException(String.Format("SqImgLayer: Index out of bounds: x={0}, y={1}, Bound=((0,{2}),(0,{3}))", x, y, Width, Height));
                }
                return layer[x,y];
            }
            set
            {
                layer[x,y] = value;
            }
        }

        protected void SetRels()
        {
            foreach (SqRt sqrt in layer)
            {
                sqrt.InitializeLayerRelations();
            }
        }

        public bool ValidateXY(int x, int y)
        {
            if ( (x>=0)&&(y>=0)&&(x<Width)&&(y<Height))
            {
                return true;
            }
            else
            {
                return false;
            }
        }


        public IEnumerator<SqRt> GetEnumerator()
        {
            return new SqImgLayerEnumerator(this);
        }

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return new SqImgLayerEnumerator(this);
        }

        public class SqImgLayerEnumerator : IEnumerator, IEnumerator<SqRt>, IDisposable
        {
            private SqImgLayer sqImgLayer;
            private IEnumerator<XY> iter;

            public SqImgLayerEnumerator(SqImgLayer sqImgLayer)
            {
                this.sqImgLayer = sqImgLayer;
                Reset();
            }

            object IEnumerator.Current
            {
                get
                {
                    return (object)(sqImgLayer[iter.Current.X, iter.Current.Y]);
                }
            }

            SqRt IEnumerator<SqRt>.Current
            {
                get
                {
                    return sqImgLayer[iter.Current.X, iter.Current.Y];
                }
            }

            public bool MoveNext()
            {
                return iter.MoveNext();
            }

            public void Reset()
            {
                iter = xyGen(sqImgLayer.Width, sqImgLayer.Height).GetEnumerator();
            }

            public void Dispose()
            {
                if (sqImgLayer != null)
                {
                    sqImgLayer.Dispose();
                }
            }
        }

        public void Dispose()
        {
            // Do nothing - no disposable resources
        }

        internal void SetNearest()
        {
            foreach (var sqrt in this)
            {
                sqrt.GetNearest();
            }
        }


        public void NearImpulse()
        {
            foreach (var v in this)
            {
                v.NeighbourGain();
            }
        }

        public Dictionary<int, SqRtGroup> SqRtGroups = new Dictionary<int, SqRtGroup>();

        internal void NearImpulse2()
        {
            foreach (var v in this)
            {
                v.NeighbourGain2();
            }            
        }

        internal void NearImpulse2x()
        {
            foreach (var v in this)
            {
                v.NeighbourColorGain = v.NeighbourColorGain2;
            }
        }

        internal void NearImpulseFeedBack()
        {
            foreach (var v in this)
            {
                v.NegativeFeedbackColorGain();
            }
        }

        internal void NeighbourColorGain()
        {
            foreach (var v in this)
            {
                v.NeighbourGainEasy1();
            }
            foreach (var v in this)
            {
                v.NeighbourGainEasy2();
            }

            //ElementRingOverLayerIterator iterator = new ElementRingOverLayerIterator(this, 20, 20, 1);
            //foreach ( var el in iterator)
            //{
            //    el.IsNCG = true ;
            //}

            while (this.Any((item) => !item.IsNCG))
            //for ( int i =0; i<2; i++)
            {
                var r = this.ArgMin((item) => item.IsNCG ? 1000 : item.NCG2);
                r.IsNCG = true;
                r.IsNCGMax = true;
                SetGroup(r);
            }

        }

        public static SqImgLayer GetLayer()
        {
            Bitmap bitmap = TestImages.GetTestImage(0);
            SqImgLayer layer = new SqImgLayer(bitmap);

            // Program #1
            layer.SetNearest();
            //layer.NearImpulse();
            //layer.NearImpulse2();
            //layer.NearImpulseFeedBack();

            // Program #2
            layer.NeighbourColorGain();

            layer.InitAreas();

            for (int i = 0; i < 13; i++)
            {
                Console.Out.WriteLine("Iteration "+i.ToString());
                layer.IterateAreaEating();
            }

            for (int i = 0; i < 6000; i++)
            {
                Console.Out.WriteLine("Iteration2 " + i.ToString());
                //layer.IterateAreaEating2();
                layer.IterateAreaEatingThem();
                layer.IterateAreaEating();
            }


            return layer;
        }

        private void IterateAreaEatingThem()
        {
            List<Tuple<SqRt, SqRt, float>> list = new List<Tuple<SqRt, SqRt, float>>();
            foreach (var area in Areas)
            {
                list.AddRange( area.AddMinNCGElementThem() );
            }
            if (list.Count() > 0)
            {
                var mostMinItem = list.ArgMin((item) => item.Item3);
                mostMinItem.Item1.Area.AddElement(mostMinItem.Item2);
            }
        }

        private void IterateAreaEating()
        {
            foreach (var area in Areas)
            {
                area.AddMinNCGElement();
            }
        }

        private void IterateAreaEating2()
        {
            foreach (var area in Areas)
            {
                area.AddNCGElement();
            }
        }

        public List<Area> Areas = new List<Area>();

        private void InitAreas()
        {
            foreach (var el in listOfGroupOwners)
            {
                Areas.Add(new Area(el));
            }
        }

        public List<SqRt> listOfGroupOwners = new List<SqRt>();

        private void SetGroup(SqRt r)
        {
            listOfGroupOwners.Add(r);

            int x = r.X;
            int y = r.Y;

            ElementRingOverLayerIterator iterator = new ElementRingOverLayerIterator(this, x, y, 2);
            foreach (var el in iterator)
            {
                el.IsNCG = true;
                el.NCGOwner = r;
            }

        }
    }
}
